#pragma once

#include <iostream>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cufft.h>
#include <stdlib.h>

struct cuda_exception {};

#define cuda(Call) do{\
	cudaError err = cuda ## Call ; \
	if (err != cudaSuccess){ \
		std::cerr << __FILE__ << ", " << __LINE__ << ": " << cudaGetErrorString(err)\
		<< std::endl << "        \"" << #Call << "\"" << std::endl;\
		throw cuda_exception(); \
	}\
	err = cudaThreadSynchronize();\
	if (err != cudaSuccess){ \
		std::cerr << __FILE__ << ", " << __LINE__ << ": " << cudaGetErrorString(err)\
		<< std::endl << "        \"" << #Call << "\"" << std::endl;\
		throw cuda_exception(); \
	}\
} while (0)

static inline char const* cufft_errstring(cufftResult err){
    switch (err){
      case CUFFT_SUCCESS: return "Success";
      case CUFFT_INVALID_PLAN: return "Invalid plan";
      case CUFFT_ALLOC_FAILED: return "Alloc failed";
      case CUFFT_INVALID_TYPE: return "Invalid type";
      case CUFFT_INVALID_VALUE: return "Invalid value";
      case CUFFT_INTERNAL_ERROR: return "Internal error";
      case CUFFT_EXEC_FAILED: return "Exec failed";
      case CUFFT_SETUP_FAILED: return "Setup failed";
      //case CUFFT_SHUTDOWN_FAILED: return "Shutdown failed";
      case CUFFT_INVALID_SIZE: return "Invalid size";
      default: return "Unknown CUFFT Error";
    }
}

#define cufft(Call) do{\
	cufftResult res = cufft ## Call ; \
	if (res != CUFFT_SUCCESS){ \
		std::cerr << __FILE__ << ", " << __LINE__ \
			<< ": " << cufft_errstring(res) \
			<< std::endl << "        \"" << #Call << "\"" << std::endl;\
		throw cuda_exception(); \
	}\
	cudaError err = cudaThreadSynchronize();\
	if (err != cudaSuccess){ \
		std::cerr << __FILE__ << ", " << __LINE__ << ": " << cudaGetErrorString(err)\
		<< std::endl << "        \"" << #Call << "\"" << std::endl;\
		throw cuda_exception(); \
	}\
} while (0)

#ifdef __CUDACC__

static inline __device__ void barrier(){
	//__threadfence_block ();
	__syncthreads ();
}

#endif

